﻿using System.Collections.Generic;
using System.Threading;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using xEngine.DataTypes;
using xEngine.Debugging;

namespace xEngine.Actors.Terrain
{
    public class TerrainQuadTree : QuadTree<TerrainQuadTreeNode, TerrainQuadTree>
    {
        #region Member Variables

        private Vector3 _position;

        private IndexBuffer[] _iBuffers;

        private bool _activeBuffer;
        private int[] _indicesCount;
        private int[] _indices;        

        #endregion

        #region Properties

        public Terrain Terrain { get; private set; }
        public TerrainVertexCollection Vertices { get; private set; }

        public VertexBuffer VertexBuffer { get; private set; }
        public IndexBuffer IndexBuffer { get { return _iBuffers[_activeBuffer ? 0 : 1]; } }
        public int IndicesCount { get { return _indicesCount[_activeBuffer ? 0 : 1]; } }

        #endregion

        #region Constructors

        public TerrainQuadTree(Terrain terrain)
        {
            Terrain = terrain;
            Vertices = new TerrainVertexCollection(Terrain.HeightMap);
            RootNode = new TerrainQuadTreeNode(QuadTreeNode.NodePosition.SingleNode, Terrain.HeightMap.Width - 1, null, this, 0);

            VertexBuffer = new VertexBuffer(terrain.GraphicsDevice, Vertices.VertexDeclaration, Vertices.VertexCount, BufferUsage.WriteOnly);
            VertexBuffer.SetData(Vertices.Vertices);

            int iSize = (terrain.HeightMap.Width - 1) * (terrain.HeightMap.Width - 1) * 6;

            _iBuffers = new IndexBuffer[] {
                new DynamicIndexBuffer(terrain.GraphicsDevice, typeof(int), iSize, BufferUsage.WriteOnly),
                new DynamicIndexBuffer(terrain.GraphicsDevice, typeof(int), iSize, BufferUsage.WriteOnly)
            };

            _indicesCount = new int[2];
            _indices = new int[iSize];
        }

        #endregion

        #region Functions

        public void AddIndex(int index)
        {
            int i = _indicesCount[_activeBuffer ? 1 : 0]++;
            
            if(i < _indices.Length)
                _indices[i] = index;
        }
        
        public void UpdateAsync()
        {
            if (RootNode == null)
                return;

            _indicesCount[_activeBuffer ? 1 : 0] = 0;

            RootNode.ResetNode();
            RootNode.SplitDownwards(Terrain.MinQuality);
            //RootNode.CalculateDepth(_position);
            RootNode.CalculateDepth(new BoundingSphere(_position, Terrain.NearDistance), new BoundingSphere(_position, Terrain.FarDistance));
            RootNode.GetEnabledVertices();

            if (!_iBuffers[_activeBuffer ? 1 : 0].IsDisposed)
                try { _iBuffers[_activeBuffer ? 1 : 0].SetData<int>(_indices, 0, _indicesCount[_activeBuffer ? 1 : 0]); } catch { }
            _activeBuffer = !_activeBuffer;
        }
        public void Update(Vector3 position)
        {
            _position = position;
        }
        public TerrainQuadTreeNode FindNodeContainingPoint(Vector2 p)
        {
            return RootNode.FindNodeContainingPoint(p);
        }
        public void UpdateBounds()
        {
            RootNode.UpdateBounds();
            Vertices.ResetNormals();
            Vertices.CalculateNormals(Terrain.Transformation.Scale);
            VertexBuffer.SetData(Vertices.Vertices);
        }

        #endregion
    }
}
